/** @file
@brief Internal support

This file contains macros that are used commonly throughout Pryn.
*/
#ifndef PRYN_COMMON_H
#define PRYN_COMMON_H

#include <pryn/platform.h>

// Build a function for a type, ensuring it's non-zero before executing body.
#define PrynBaseBody(TypeName, Body) \
	do { \
		PrynStartFunction (); \
		{ \
			prynAssert (TypeName, NullArgument); \
			Body \
		} \
		PrynEndFunction (); \
	} while (0)

#define PrynBaseGetProperty(Package, Type, TypeName, PropName, PropType, PropInitial, PropBody) \
	Pryn##Package##Export (PrynResult, pryn##Type##PropName, (Pryn##Type *TypeName, PropType *output)) \
	{ \
		const PrynString nullString = {0, 0}; \
		const PrynObject nullObject = {0, 0}; \
		if (output) *output = PropInitial; \
		Pryn##Type##Body ({ \
			prynAssert (output, NullArgument); \
			PropBody \
		}); \
	}

#define PrynBaseSetProperty(Package, Type, TypeName, PropName, PropType, PropBody) \
	Pryn##Package##Export (PrynResult, pryn##Type##Set##PropName, (Pryn##Type *TypeName, PropType value)) \
	{ \
		Pryn##Type##Body ({ \
			PropBody \
		}); \
	}

#define PrynStartFunctionHasState() \
	PrynResult result = PrynResultSuccess

#define PrynStartFunction() \
	PrynStartFunctionHasState (); \
	PrynState *state = 0

#define PrynEndFunctionAfter(ON_SUCCESS, ON_FAILURE, ON_COMMON) \
	result = PrynResultSuccess; \
success: \
	ON_SUCCESS \
	if (0) { failure:; ON_FAILURE } \
	ON_COMMON \
	return result

#define PrynEndFunction() PrynEndFunctionAfter ( , , )
#define PrynEndFunctionAfterFailure(ON_FAILURE) PrynEndFunctionAfter ( , ON_FAILURE, )
#define PrynEndFunctionAfterSuccess(ON_SUCCESS) PrynEndFunctionAfter (ON_SUCCESS, , )
#define PrynEndFunctionAfterCommon(ON_COMMON) PrynEndFunctionAfter ( , , ON_COMMON)

#define prynReturnCall(RESULT) do { prynCall (RESULT); goto success; } while (0)
#define prynReturnResult() do { if (result < 0) goto failure; goto success; } while (0)
#define prynReturnSuccess() prynReturnSuccessCode (Success)
#define prynReturnTrue() prynReturnSuccessCode (True)
#define prynReturnSuccessCode(CODE) do { result = PrynResult##CODE; goto success; } while (0)
#define prynReturnFailure(CODE) do { result = PrynResult##CODE; goto failure; } while (0)
#define prynReturnFalse() prynReturnSuccessCode (False)
#define prynReturnDone() prynReturnSuccessCode (Done)
#define prynReturnBoolean(TEST) do { result = (TEST) ? PrynResultSuccess : PrynResultDone; goto success; } while (0)

#define prynCall(FUNCTION) do { if ((result = (FUNCTION)) < 0) goto failure; } while (0)

/** If @e TEST is false, then assign @e result to @e PrynResult + @e RESULT_ON_FAILURE and @e goto @e failure. For example:

@code
PrynResult result = 0;

object = prynAllocate (state, 100000);
prynAssert (object, OutOfMemory);

if (0) { failure:
	// Perform anything needed during failure.
}

return result;
@endcode
*/
#define prynAssert(TEST, RESULT_ON_FAILURE) do { if (!(TEST)) { result = PrynResult##RESULT_ON_FAILURE; goto failure; } } while (0)

/// Evaluates to true if this is a true/success value.
#define PrynIsTrue(VALUE) ((VALUE) == 0)

/// Evaluates to true if this is an error result condition.
#define PrynIsFailure(VALUE) ((VALUE) < 0)

/// Define a double-linked list in a structure. TYPE is the type of object to store (excluding pointer).
#define PrynDList(TYPE) struct { TYPE *first, *last; }

#define PrynDListTypedef(TYPE, NAME) typedef struct NAME { TYPE *first, *last; } NAME 

#define PrynCallbackTypedef(RETURNS, NAME, ARGUMENTS) PrynCSectionStart typedef RETURNS (*NAME) ARGUMENTS; PrynCSectionEnd
	/**< @brief Define a new callback typedef. The arguments needs to include a data pointer. */

#define PrynCallback(TYPE, NAME) \
	void *NAME##Data; \
	void (*NAME##Destructor) (void *data); \
	TYPE NAME
	/**< @brief Define a new callback field. This includes the callback itself, the data field which is NAME+Data, and the destructor which is NAME+DestroyData. */

/// Assign the callback fields.
#define PrynAssignCallback(NAME, VALUE, DATA, DESTRUCTOR) \
	do { \
		PrynDestroyCallback (NAME); \
		NAME = VALUE; \
		NAME##Data = DATA; \
		NAME##Destructor = DESTRUCTOR; \
	} while (0)

#define PrynDestroyCallback(NAME) do { if (NAME##Destructor) NAME##Destructor (NAME##Data); } while (0)

#define PrynCallbackWrapperDeclaration(TYPE, NAME, PROPNAME, STATE, ARGUMENTS, CALL) \
	typedef void (*NAME) ARGUMENTS; \
	\
	struct NAME##Wrapper \
	{ \
		PrynState *state; /**< State for destroying the wrapper. */ \
		NAME function; /**< Function pointer. */ \
		void *data; /**< Data pointer. */ \
		void (*destructor) (void *data); /**< Destroys data pointer or 0 for none. */ \
	}; \
	\
	static inline PrynResult NAME##WrapperFunction ARGUMENTS; \
	\
	inline void PROPNAME (NAME value, void *data = 0, void (*destroy) (void *data) = 0);

#define PrynCallbackWrapperBodyCommon(TYPE, NAME, PROPNAME, STATE, ARGUMENTS, CALL) \
	inline void Pryn##TYPE::PROPNAME (NAME value, void *data, void (*destroy) (void *data)) \
	{ \
		NAME##Wrapper *wrapper; \
		PrynState *state = STATE; \
		PrynResult result; \
		\
		if (!value) \
		{ \
			pryn##TYPE##Set##NAME (this, 0, data, destroy); \
			return; \
		} \
		\
		result = prynAllocateClearedObject (state, &wrapper), prynCheckErrorBase (state, result); \
		wrapper->state = state; \
		wrapper->function = value; \
		wrapper->data = data; \
		wrapper->destructor = destroy; \
		pryn##TYPE##Set##NAME (this, &NAME##WrapperFunction, wrapper, &PrynWrapperDelete); \
	}

#define PrynCallbackWrapperBody(TYPE, NAME, PROPNAME, STATE, ARGUMENTS, CALL) \
	inline PrynResult Pryn##TYPE::NAME##WrapperFunction ARGUMENTS \
	{ \
		NAME##Wrapper *wrapper = (NAME##Wrapper *) data; \
		PrynState *state = wrapper->state; \
		\
		try \
		{ \
			data = wrapper->data; \
			wrapper->function CALL; \
		} \
		catch (PrynException &exception) \
		{ \
			return exception.result; \
		} \
		\
		return PrynResultSuccess; \
	} \
	\
	PrynCallbackWrapperBodyCommon (TYPE, NAME, PROPNAME, STATE, ARGUMENTS, CALL)

#define PrynBoolCallbackWrapperBody(TYPE, NAME, PROPNAME, STATE, ARGUMENTS, CALL) \
	inline PrynResult Pryn##TYPE::NAME##WrapperFunction ARGUMENTS \
	{ \
		NAME##Wrapper *wrapper = (NAME##Wrapper *) data; \
		PrynState *state = wrapper->state; \
		bool result; \
		\
		try \
		{ \
			data = wrapper->data; \
			result = wrapper->function CALL; \
		} \
		catch (PrynException &exception) \
		{ \
			return exception.result; \
		} \
		\
		return result ? PrynResultTrue : PrynResultFalse; \
	} \
	\
	PrynCallbackWrapperBodyCommon (TYPE, NAME, PROPNAME, STATE, ARGUMENTS, CALL)

#define PrynBoolCallbackWrapperDeclaration(TYPE, NAME, PROPNAME, STATE, ARGUMENTS, CALL) \
	typedef bool (*NAME) ARGUMENTS; \
	\
	struct NAME##Wrapper \
	{ \
		PrynState *state; /**< State for destroying the wrapper. */ \
		NAME function; /**< Function pointer. */ \
		void *data; /**< Data pointer. */ \
		void (*destructor) (void *data); /**< Destroys data pointer or 0 for none. */ \
	}; \
	\
	static inline PrynResult NAME##WrapperFunction ARGUMENTS; \
	\
	inline void PROPNAME (NAME value, void *data = 0, void (*destroy) (void *data) = 0);

//PrynReturnCallbackWrapperBody (Factory, DisplayNameFunction, displayNameFunction, this->state (), (PrynComponent *component, PrynString *value, void *data), (component, data), (PrynComponent *component, void *data), PrynString, value)

#define PrynReturnCallbackWrapperBody(TYPE, NAME, PROPNAME, STATE, ARGUMENTS, CALL, FUNCTION_ARGUMENTS, RETURN_TYPE, RETURN_ARGUMENT) \
	inline PrynResult Pryn##TYPE::NAME##WrapperFunction ARGUMENTS \
	{ \
		NAME##Wrapper *wrapper = (NAME##Wrapper *) data; \
		PrynState *state = wrapper->state; \
		\
		try \
		{ \
			data = wrapper->data; \
			*RETURN_ARGUMENT = wrapper->function CALL; \
		} \
		catch (PrynException &exception) \
		{ \
			return exception.result; \
		} \
		\
		return PrynResultSuccess; \
	} \
	\
	inline void Pryn##TYPE::PROPNAME (NAME value, void *data, void (*destroy) (void *data)) \
	{ \
		NAME##Wrapper *wrapper; \
		PrynState *state = STATE; \
		PrynResult result; \
		\
		if (!value) \
		{ \
			pryn##TYPE##Set##NAME (this, 0, data, destroy); \
			return; \
		} \
		\
		result = prynAllocateClearedObject (state, &wrapper), prynCheckErrorBase (state, result); \
		wrapper->state = state; \
		wrapper->function = value; \
		wrapper->data = data; \
		wrapper->destructor = destroy; \
		pryn##TYPE##Set##NAME (this, &NAME##WrapperFunction, wrapper, &PrynWrapperDelete); \
	}

#define PrynReturnCallbackWrapperDeclaration(TYPE, NAME, PROPNAME, STATE, ARGUMENTS, CALL, FUNCTION_ARGUMENTS, RETURN_TYPE, RETURN_ARGUMENT) \
	typedef RETURN_TYPE (*NAME) FUNCTION_ARGUMENTS; \
	\
	struct NAME##Wrapper \
	{ \
		PrynState *state; /**< State for destroying the wrapper. */ \
		NAME function; /**< Function pointer. */ \
		void *data; /**< Data pointer. */ \
		void (*destructor) (void *data); /**< Destroys data pointer or 0 for none. */ \
	}; \
	\
	static inline PrynResult NAME##WrapperFunction ARGUMENTS; \
	\
	inline void PROPNAME (NAME value, void *data = 0, void (*destroy) (void *data) = 0);

#define PrynSetCallbackDefinition(PACKAGE, TYPE, NAME, STATE, DEFAULT) \
	Pryn##PACKAGE##Export (PrynResult, pryn##TYPE##Set##NAME, (Pryn##TYPE *self, Pryn##TYPE##NAME value, void *data, void (*destroyData) (void *data))) \
	{ \
		if (!value) value = DEFAULT; \
		if (!self || !value) { if (destroyData) destroyData (data); return PrynResultNullArgument; } \
		PrynAssignCallback (self->p##NAME, value, data, destroyData); \
		return PrynResultSuccess; \
	}

PrynCallbackTypedef (void, PrynDeleteDataFunction, (void *data))
	/**< @brief Data deletion function for callbacks. */

#define PrynImportSetCallback(PACKAGE, TYPE, NAME) Pryn##PACKAGE##Import (PrynResult, pryn##TYPE##Set##NAME, (Pryn##TYPE *self, Pryn##TYPE##NAME value, void *data, PrynDeleteDataFunction destroyData))

#if __cplusplus
struct PrynState;
enum PrynResult;

/// A C++-only function that checks for whether the result is an error code. If it is, then an exception is thrown.
//inline PrynResult prynCheckError (PrynState *state, PrynResult result);
inline PrynResult prynThrow (PrynState *state, PrynResult result);

/// A C++-only macro that throws an error if RESULT is below zero. RESULT is evaluated twice.
#define prynCheckErrorBase(STATE, RESULT) ((RESULT) < 0 ? prynThrow (STATE, (RESULT)) : (RESULT))

/// Check error with a null state.
inline PrynResult prynCheckErrorNull (PrynResult result) { return prynCheckErrorBase (0, result); }
#endif /* __cplusplus */

typedef struct PrynCommonObject PrynCommonObject;
typedef struct PrynCommonResource PrynCommonResource;

typedef enum PrynObjectType PrynObjectType;

typedef struct PrynTag PrynTag; ///< A user-defined piece of data that can be attached to most objects.
typedef PrynDList (PrynTag) PrynTagList; ///< List of tags.

typedef struct PrynMonitor PrynMonitor;

/// A list of monitors.
PrynDListTypedef (PrynMonitor, PrynMonitorList);

/// Common fields you'd have in any object.
struct PrynCommonObject
{
	PrynObjectType pType; ///< Type of the object contained.
	PrynTagList pTags; ///< List of tags associated with the type.
	PrynMonitorList pMonitors; ///< List of monitors associated with the type.
	bool pMark; ///< Used for various temporary purposes.
};

#define prynCommonObjectDestroy(COMMON, STATE) \
	do { \
		PrynCommonObject *POINTER = (COMMON); \
		PrynState *STATEP = (STATE); \
		\
		prynMonitorListDestroy (&POINTER->pMonitors); \
		prynTagListDestroy (&POINTER->pTags); \
	} while (0)

#define prynCommonResourceSetName(COMMON, VALUE) do { PrynCommonResource *POINTER = (COMMON); if (POINTER->pName.pointer) POINTER->pNameChanged = true; prynStringOverwrite (&POINTER->pName, state, (VALUE)); } while (0)
#define prynCommonResourceSetDescription(COMMON, VALUE) do { PrynCommonResource *POINTER = (COMMON); if (POINTER->pDescription.pointer) POINTER->pDescriptionChanged = true; prynStringOverwrite (&POINTER->pDescription, state, (VALUE)); } while (0)

#define prynCommonResourceDestroy(COMMON, STATE) do { PrynCommonResource *POINTER = (COMMON); PrynState *STATEP = (STATE); prynStringFree (&POINTER->pName, STATEP); prynStringFree (&POINTER->pDescription, STATEP); POINTER->pDescriptionChanged = false; POINTER->pNameChanged = false; } while (0)

#include <pryn/string.h>

/// Common fields you'd have in a resource.
struct PrynCommonResource
{
	PrynString pName; /**< Localised or customised name of the object, such as "Image". */ \
	PrynString pDescription; /**< Localised or customised description of the object, such as "A pixel graphics image.". */ \
	bool pNameChanged; /**< Whether the name field has been customised. */ \
	bool pDescriptionChanged; /**< Whether the description field has been customised. */
};

#endif /* PRYN_COMMON_H */